import os
import subprocess
from PIL import Image, ImageSequence, ImageOps
import folder_paths
import time
from datetime import datetime
import torch
import numpy as np
import signal
import threading
import hashlib
import node_helpers

class ModifyPSDLayer:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "photoshop_path": ("STRING", {"default": r"请填写Photoshop路径:photoshop.exe的完整路径"}),
                "psd_path": ("STRING", {"multiline": False}),
                "output_name": ("STRING", {"default": ""}),
                "export_png": ("BOOLEAN", {"default": True}),
                "close_other_docs": ("BOOLEAN", {"default": True}),

                # Text layers
                "text_layer_name_1": ("STRING", {"default": ""}),
                "text_content_1": ("STRING", {"default": ""}),
                "text_offset_1": ("STRING", {"default": "0 0 0 0", "multiline": False}),
                "text_layer_name_2": ("STRING", {"default": ""}),
                "text_content_2": ("STRING", {"default": ""}),
                "text_offset_2": ("STRING", {"default": "0 0 0 0", "multiline": False}),
                "text_layer_name_3": ("STRING", {"default": ""}),
                "text_content_3": ("STRING", {"default": ""}),
                "text_offset_3": ("STRING", {"default": "0 0 0 0", "multiline": False}),

                # Image layer names
                "image_layer_name_1": ("STRING", {"default": ""}),
                "image_offset_1": ("STRING", {"default": "0 0 0 0"}),
                "image_layer_name_2": ("STRING", {"default": ""}),
                "image_offset_2": ("STRING", {"default": "0 0 0 0"}),
                "image_layer_name_3": ("STRING", {"default": ""}),
                "image_offset_3": ("STRING", {"default": "0 0 0 0"}),
                "image_layer_name_4": ("STRING", {"default": ""}),
                "image_offset_4": ("STRING", {"default": "0 0 0 0"}),

                 # 独立的图像1智能处理参数
                "use_fill_strategy_1": ("BOOLEAN", {"default": False, "label": "图像1 - 使用填充策略（最大化覆盖）", "description": "勾选后使用最大比例因子，不勾选使用最小比例因子（完全包含）"}),
                "crop_strategy_1": ("COMBO", {"default": "center", "options": ["center", "bottom"], "label": "图像1 - 裁剪策略", "description": "center=居中裁剪, bottom=底部裁剪(保留顶部头像)"}),
                
                # 独立的图像2智能处理参数
                "use_fill_strategy_2": ("BOOLEAN", {"default": False, "label": "图像2 - 使用填充策略（最大化覆盖）", "description": "勾选后使用最大比例因子，不勾选使用最小比例因子（完全包含）"}),
                "crop_strategy_2": ("COMBO", {"default": "center", "options": ["center", "bottom"], "label": "图像2 - 裁剪策略", "description": "center=居中裁剪, bottom=底部裁剪(保留顶部头像)"}),
                
                # 独立的图像3智能处理参数
                "use_fill_strategy_3": ("BOOLEAN", {"default": False, "label": "图像3 - 使用填充策略（最大化覆盖）", "description": "勾选后使用最大比例因子，不勾选使用最小比例因子（完全包含）"}),
                "crop_strategy_3": ("COMBO", {"default": "center", "options": ["center", "bottom"], "label": "图像3 - 裁剪策略", "description": "center=居中裁剪, bottom=底部裁剪(保留顶部头像)"}),
                
                # 独立的图像4智能处理参数
                "use_fill_strategy_4": ("BOOLEAN", {"default": False, "label": "图像4 - 使用填充策略（最大化覆盖）", "description": "勾选后使用最大比例因子，不勾选使用最小比例因子（完全包含）"}),
                "crop_strategy_4": ("COMBO", {"default": "center", "options": ["center", "bottom"], "label": "图像4 - 裁剪策略", "description": "center=居中裁剪, bottom=底部裁剪(保留顶部头像)"}),
            },
            "optional": {
                # Image inputs (optional)
                "image_1": ("IMAGE",),
                "image_2": ("IMAGE",),
                "image_3": ("IMAGE",),
                "image_4": ("IMAGE",),
            }
        }

    RETURN_TYPES = ("IMAGE",)
    RETURN_NAMES = ("final_preview",)
    FUNCTION = "run"
    CATEGORY = "psd"

    def save_image(self, tensor_image, name):
        # 检查tensor的维度
        tensor_np = tensor_image[0].numpy()
        print(f"=== 图像 {name} 详细信息 ===")
        print(f"输入tensor形状: {tensor_np.shape}")
        print(f"tensor数据类型: {tensor_np.dtype}")
        print(f"tensor值范围: [{tensor_np.min():.4f}, {tensor_np.max():.4f}]")
        
        # 检查是否有alpha通道信息
        if len(tensor_np.shape) == 3:
            if tensor_np.shape[0] in [3, 4]:  # (C, H, W) 格式
                print(f"检测到 (C, H, W) 格式，通道数: {tensor_np.shape[0]}")
                if tensor_np.shape[0] == 4:  # RGBA
                    print("检测到RGBA图像，保留alpha通道")
                    print(f"Alpha通道值范围: [{tensor_np[3].min():.4f}, {tensor_np[3].max():.4f}]")
                    # 转换为RGBA格式 (C, H, W) -> (H, W, C)
                    image_array = np.transpose(tensor_np, (1, 2, 0))
                    image_array = (image_array * 255).astype("uint8")
                    image = Image.fromarray(image_array, 'RGBA')
                else:  # RGB
                    print("检测到RGB图像")
                    # 转换为RGB格式 (C, H, W) -> (H, W, C)
                    image_array = np.transpose(tensor_np, (1, 2, 0))
                    image_array = (image_array * 255).astype("uint8")
                    image = Image.fromarray(image_array, 'RGB')
            elif tensor_np.shape[2] in [3, 4]:  # (H, W, C) 格式
                print(f"检测到 (H, W, C) 格式，通道数: {tensor_np.shape[2]}")
                if tensor_np.shape[2] == 4:  # RGBA
                    print("检测到RGBA图像，保留alpha通道")
                    print(f"Alpha通道值范围: [{tensor_np[:,:,3].min():.4f}, {tensor_np[:,:,3].max():.4f}]")
                    image_array = (tensor_np * 255).astype("uint8")
                    image = Image.fromarray(image_array, 'RGBA')
                else:  # RGB
                    print("检测到RGB图像")
                    # 检查是否在RGB中有透明信息（比如白色背景）
                    r, g, b = tensor_np[:,:,0], tensor_np[:,:,1], tensor_np[:,:,2]
                    print(f"RGB通道值范围: R[{r.min():.4f}, {r.max():.4f}], G[{g.min():.4f}, {g.max():.4f}], B[{b.min():.4f}, {b.max():.4f}]")
                    
                    # 检查是否有白色背景（可能是透明区域）
                    white_threshold = 0.95  # 接近白色的阈值
                    white_pixels = np.sum((r > white_threshold) & (g > white_threshold) & (b > white_threshold))
                    total_pixels = r.size
                    white_ratio = white_pixels / total_pixels
                    print(f"白色像素比例: {white_ratio:.4f} ({white_pixels}/{total_pixels})")
                    
                    # 如果白色像素比例很高，可能是透明背景
                    if white_ratio > 0.5:
                        print("检测到大量白色像素，可能是透明背景")
                        # 尝试创建透明背景
                        # 将白色区域设为透明
                        alpha = np.ones((tensor_np.shape[0], tensor_np.shape[1]))
                        white_mask = (r > white_threshold) & (g > white_threshold) & (b > white_threshold)
                        alpha[white_mask] = 0
                        
                        # 创建RGBA图像
                        rgba = np.stack([r, g, b, alpha], axis=2)
                        image_array = (rgba * 255).astype("uint8")
                        image = Image.fromarray(image_array, 'RGBA')
                        print("已创建RGBA图像，白色背景设为透明")
                    else:
                        image_array = (tensor_np * 255).astype("uint8")
                        image = Image.fromarray(image_array, 'RGB')
                        print("保持RGB格式")
            else:
                print(f"警告: 未知的tensor格式 {tensor_np.shape}")
                # 尝试直接处理
                image_array = (tensor_np * 255).astype("uint8")
                image = Image.fromarray(image_array)
        else:
            print(f"警告: 不支持的tensor维度 {len(tensor_np.shape)}")
            return None
        
        out_dir = os.path.join(folder_paths.get_temp_directory(), "psd_img_replace")
        os.makedirs(out_dir, exist_ok=True)
        out_path = os.path.join(out_dir, f"{name}.png")
        
        # 保存为PNG以保持透明度
        image.save(out_path, 'PNG')
        print(f"图像已保存: {out_path}, 模式: {image.mode}")
        
        # 验证保存的图像
        try:
            saved_image = Image.open(out_path)
            print(f"验证保存的图像: 模式={saved_image.mode}, 大小={saved_image.size}")
            if saved_image.mode == 'RGBA':
                # 检查alpha通道
                alpha_array = np.array(saved_image.split()[-1])  # 获取alpha通道
                transparent_pixels = np.sum(alpha_array == 0)
                total_pixels = alpha_array.size
                print(f"透明像素比例: {transparent_pixels/total_pixels:.4f} ({transparent_pixels}/{total_pixels})")
        except Exception as e:
            print(f"验证保存图像时出错: {e}")
        
        print("=== 图像处理完成 ===")
        
        # 关于alpha通道的建议
        if len(tensor_np.shape) == 3 and tensor_np.shape[2] == 3:
            print("注意: 输入图像只有RGB通道，没有alpha通道")
            print("建议: 如果原图是透明PNG，请检查ComfyUI前面的节点是否正确保留了alpha通道")
            print("可能的解决方案:")
            print("1. 使用LoadImage节点加载PNG时确保选择'RGBA'模式")
            print("2. 检查图像处理节点是否支持alpha通道")
            print("3. 确保没有使用会丢失alpha通道的图像处理节点")
        
        return out_path
    
    def parse_layer_names(self, stdout_text):
        """解析Photoshop输出的图层名称"""
        layer_names = []
        lines = stdout_text.split('\n')
        in_layer_section = False
        
        for line in lines:
            line = line.strip()
            if line == "=== LAYER_NAMES_START ===":
                in_layer_section = True
                continue
            elif line == "=== LAYER_NAMES_END ===":
                break
            elif in_layer_section and line:
                layer_names.append(line)
        
        return layer_names

    def run(self,
            photoshop_path, psd_path, output_name, export_png, close_other_docs,
            use_fill_strategy_1, crop_strategy_1,
            use_fill_strategy_2, crop_strategy_2,
            use_fill_strategy_3, crop_strategy_3,
            use_fill_strategy_4, crop_strategy_4,
            text_layer_name_1, text_content_1, text_offset_1,
            text_layer_name_2, text_content_2, text_offset_2,
            text_layer_name_3, text_content_3, text_offset_3,
            image_layer_name_1, image_offset_1, image_layer_name_2, image_offset_2, 
            image_layer_name_3, image_offset_3, image_layer_name_4, image_offset_4,
            image_1=None, image_2=None, image_3=None, image_4=None,
            ):
        base_dir = os.path.dirname(psd_path)
        base_name = os.path.splitext(os.path.basename(psd_path))[0]

        # 自动生成输出文件名
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        out_psd_name = output_name if output_name.strip() else f"{base_name}_{timestamp}"
        out_psd_path = os.path.join(base_dir, out_psd_name + ".psd")
        out_png_path = os.path.join(base_dir, out_psd_name + ".png")

        # 收集文本替换
        text_replacements = []
        for name, content, offset in [
            (text_layer_name_1, text_content_1, text_offset_1),
            (text_layer_name_2, text_content_2, text_offset_2),
            (text_layer_name_3, text_content_3, text_offset_3),
        ]:
            if name.strip():
                # 解析偏移参数
                offset_parts = offset.strip().split()
                if len(offset_parts) == 4:
                    try:
                        offset_x = float(offset_parts[0])  # 水平偏移
                        offset_y = float(offset_parts[1])  # 垂直偏移
                        scale_x = float(offset_parts[2])   # X方向缩放
                        scale_y = float(offset_parts[3])   # Y方向缩放
                    except ValueError:
                        print(f"警告: 文本偏移参数格式错误 '{offset}'，使用默认值")
                        offset_x, offset_y, scale_x, scale_y = 0.0, 0.0, 0.0, 0.0
                else:
                    print(f"警告: 文本偏移参数格式错误 '{offset}'，使用默认值")
                    offset_x, offset_y, scale_x, scale_y = 0.0, 0.0, 0.0, 0.0
                
                text_replacements.append({
                    "name": name, 
                    "content": content,
                    "offset_x": offset_x,
                    "offset_y": offset_y,
                    "scale_x": scale_x,
                    "scale_y": scale_y
                })

        # 收集图片替换
        image_replacements = []
        # 为每个图像准备独立的智能处理参数
        fill_strategies = [use_fill_strategy_1, use_fill_strategy_2, use_fill_strategy_3, use_fill_strategy_4]
        crop_strategies = [crop_strategy_1, crop_strategy_2, crop_strategy_3, crop_strategy_4]
        
        for idx, (name, image, offset) in enumerate([
            (image_layer_name_1, image_1, image_offset_1),
            (image_layer_name_2, image_2, image_offset_2),
            (image_layer_name_3, image_3, image_offset_3),
            (image_layer_name_4, image_4, image_offset_4),
        ]):
            if name.strip() and image is not None:
                # 解析偏移参数
                offset_parts = offset.strip().split()
                if len(offset_parts) == 4:
                    try:
                        offset_x = float(offset_parts[0])  # 水平偏移
                        offset_y = float(offset_parts[1])  # 垂直偏移
                        scale_x = float(offset_parts[2])   # X方向缩放
                        scale_y = float(offset_parts[3])   # Y方向缩放
                    except ValueError:
                        print(f"警告: 图像偏移参数格式错误 '{offset}'，使用默认值")
                        offset_x, offset_y, scale_x, scale_y = 0.0, 0.0, 0.0, 0.0
                else:
                    print(f"警告: 图像偏移参数格式错误 '{offset}'，使用默认值")
                    offset_x, offset_y, scale_x, scale_y = 0.0, 0.0, 0.0, 0.0
                
                image_path = self.save_image(image, f"image_{idx+1}")
                if image_path is not None:
                    image_replacements.append({
                        "name": name, 
                        "content": image_path,
                        "offset_x": offset_x,
                        "offset_y": offset_y,
                        "scale_x": scale_x,
                        "scale_y": scale_y,
                        "use_fill_strategy": str(fill_strategies[idx]).lower(),
                        "crop_strategy": crop_strategies[idx]
                    })
                else:
                    print(f"警告: 图像 {name} 保存失败，跳过此替换")
            elif name.strip() and image is None:
                print(f"跳过图像 {name}：未提供图像输入")
            elif not name.strip():
                print(f"跳过图像 {idx+1}：未指定图层名称")

        # 创建 JSX 脚本
        escaped_psd_path = psd_path.replace("\\", "\\\\")
        escaped_out_psd_path = out_psd_path.replace("\\", "\\\\")
        escaped_out_png_path = out_png_path.replace("\\", "\\\\")
        
        # 获取temp目录并创建日志文件路径
        temp_dir = folder_paths.get_temp_directory()
        os.makedirs(temp_dir, exist_ok=True)  # 确保temp目录存在
        log_path = os.path.join(temp_dir, f"psd_log_{timestamp}.txt")
        escaped_log_path = log_path.replace("\\", "\\\\")
        
        jsx_lines = [
            '#target photoshop',
            f'var logFile = new File("{escaped_log_path}");',
            'logFile.encoding = "UTF-8";',
            'logFile.open("w");',
            'logFile.writeln("=== JSX脚本开始执行 ===");',
            'logFile.writeln("时间: " + new Date().toString());',
            'logFile.writeln("日志文件路径: " + logFile.fsName);',
            '',
            'try {',
            '    logFile.writeln("检查Photoshop应用程序");',
            '    if (typeof app === "undefined") {',
            '        logFile.writeln("错误: 无法访问Photoshop应用程序");',
            '        throw new Error("Photoshop应用程序不可用");',
            '    }',
            '    logFile.writeln("Photoshop应用程序可用");',
            'logFile.writeln("应用程序名称: " + app.name);',
            'logFile.writeln("应用程序版本: " + app.version);',
            '',
            '    // 记录当前文档信息（稍后用于关闭其他文档）',
            '    var currentDocName = "";',
            '    var currentDocIndex = -1;',
            '',
            f'    logFile.writeln("正在检查PSD文件: {escaped_psd_path}");',
            f'    var psdFile = new File("{escaped_psd_path}");',
            '    logFile.writeln("PSD文件对象创建成功");',
            '    logFile.writeln("PSD文件路径: " + psdFile.fsName);',
            '    logFile.writeln("PSD文件是否存在: " + psdFile.exists);',
            '    if (!psdFile.exists) {',
            f'        logFile.writeln("错误: PSD文件不存在: {escaped_psd_path}");',
            '        throw new Error("PSD文件不存在");',
            '    }',
            '    logFile.writeln("PSD文件存在，正在打开...");',
            f'    var psd = app.open(psdFile);',
            '    logFile.writeln("PSD文件打开成功");',
            '    logFile.writeln("文档名称: " + psd.name);',
            '    logFile.writeln("文档宽度: " + psd.width);',
            '    logFile.writeln("文档高度: " + psd.height);',
            '    ',
            '    // 记录当前文档信息',
            '    currentDocName = psd.name;',
            '    logFile.writeln("当前处理文档名称: " + currentDocName);',
            '',
            '    // 递归遍历所有图层的函数',
            '    function getAllLayers(layers, prefix) {',
            '        var allLayers = new Array();',
            '        logFile.writeln("开始遍历图层集合，数量: " + layers.length);',
            '        for (var i = 0; i < layers.length; i++) {',
            '            var layer = layers[i];',
            '            var layerPath = prefix + layer.name;',
            '            logFile.writeln("检查图层: " + layerPath + " (类型: " + layer.typename + ")");',
            '            if (layer.typename === "LayerSet") {',
            '                logFile.writeln("发现图层组: " + layerPath + ", 子图层数: " + layer.layers.length);',
            '                var subLayers = getAllLayers(layer.layers, layerPath + "/");',
            '                for (var j = 0; j < subLayers.length; j++) {',
            '                    allLayers.push(subLayers[j]);',
            '                }',
            '            } else if (layer.typename === "ArtLayer") {',
            '                var visibility = layer.visible ? "可见" : "隐藏";',
            '                var locked = layer.locked ? "锁定" : "未锁定";',
            '                var layerType = "未知";',
            '                try {',
            '                    layerType = layer.kind === LayerKind.TEXT ? "文本" : "图片";',
            '                } catch (e) {',
            '                    layerType = "其他类型";',
            '                }',
            '                logFile.writeln("图层: " + layerPath + " (" + layerType + ", " + visibility + ", " + locked + ")");',
            '                var layerInfo = new Object();',
            '                layerInfo.name = layerPath;',
            '                layerInfo.layer = layer;',
            '                allLayers.push(layerInfo);',
            '            } else {',
            '                logFile.writeln("未知图层类型: " + layerPath + " (" + layer.typename + ")");',
            '            }',
            '        }',
            '        logFile.writeln("图层集合遍历完成，找到 " + allLayers.length + " 个图层");',
            '        return allLayers;',
            '    }',
            '',
            '    // 递归查找指定名称的图层',
            '    function findLayerByName(layers, targetName) {',
            '        for (var i = 0; i < layers.length; i++) {',
            '            var layer = layers[i];',
            '            if (layer.name === targetName) {',
            '                return layer;',
            '            }',
            '            if (layer.typename === "LayerSet") {',
            '                var foundLayer = findLayerByName(layer.layers, targetName);',
            '                if (foundLayer) {',
            '                    return foundLayer;',
            '                }',
            '            }',
            '        }',
            '        return null;',
            '    }',
            '',
            '    // 处理画板结构的函数',
            '    function processArtboards() {',
            '        var allLayers = new Array();',
            '        try {',
            '            if (psd.artboards && psd.artboards.length > 0) {',
            '                logFile.writeln("处理画板结构...");',
            '                for (var i = 0; i < psd.artboards.length; i++) {',
            '                    var artboard = psd.artboards[i];',
            '                    logFile.writeln("处理画板: " + artboard.name);',
            '                    var artboardLayers = getAllLayers(artboard.layers, artboard.name + "/");',
            '                    for (var j = 0; j < artboardLayers.length; j++) {',
            '                        allLayers.push(artboardLayers[j]);',
            '                    }',
            '                }',
            '            }',
            '        } catch (e) {',
            '            logFile.writeln("处理画板时出错: " + e.message);',
            '        }',
            '        return allLayers;',
            '    }',
            '',
            '    // 输出所有图层名称',
            '    logFile.writeln("=== LAYER_NAMES_START ===");',
            '    logFile.writeln("根级别图层数: " + psd.artLayers.length);',
            '    logFile.writeln("检查文档结构...");',
            '    try {',
            '        logFile.writeln("检查是否有画板...");',
            '        if (psd.artboards && psd.artboards.length > 0) {',
            '            logFile.writeln("发现画板数量: " + psd.artboards.length);',
            '            for (var i = 0; i < psd.artboards.length; i++) {',
            '                var artboard = psd.artboards[i];',
            '                logFile.writeln("画板 " + (i+1) + ": " + artboard.name);',
            '            }',
            '        } else {',
            '            logFile.writeln("没有画板");',
            '        }',
            '    } catch (e) {',
            '        logFile.writeln("检查画板时出错: " + e.message);',
            '    }',
            '    try {',
            '        logFile.writeln("检查背景图层...");',
            '        if (psd.backgroundLayer) {',
            '            logFile.writeln("发现背景图层: " + psd.backgroundLayer.name);',
            '        } else {',
            '            logFile.writeln("没有背景图层");',
            '        }',
            '    } catch (e) {',
            '        logFile.writeln("检查背景图层时出错: " + e.message);',
            '    }',
            '    logFile.writeln("检查所有图层（包括隐藏的）:");',
            '    for (var i = 0; i < psd.artLayers.length; i++) {',
            '        var layer = psd.artLayers[i];',
            '        logFile.writeln("根图层 " + (i+1) + ": " + layer.name + " (类型: " + layer.typename + ", 可见: " + layer.visible + ")");',
            '    }',
            '    logFile.writeln("递归遍历所有图层（包括组中的）:");',
            '    var allLayers = new Array();',
            '    // 先尝试处理画板结构',
            '    var artboardLayers = processArtboards();',
            '    if (artboardLayers.length > 0) {',
            '        logFile.writeln("从画板中找到 " + artboardLayers.length + " 个图层");',
            '        allLayers = artboardLayers;',
            '    }',
            '    if (allLayers.length === 0) {',
            '        // 尝试使用layers属性',
            '        if (psd.layers && psd.layers.length > 0) {',
            '            logFile.writeln("使用layers属性遍历图层");',
            '            allLayers = getAllLayers(psd.layers, "");',
            '        }',
            '    }',
            '    if (allLayers.length === 0) {',
            '        // 最后尝试使用artLayers属性',
            '        logFile.writeln("使用artLayers属性遍历图层");',
            '        allLayers = getAllLayers(psd.artLayers, "");',
            '    }',
            '    logFile.writeln("总图层数: " + allLayers.length);',
            '    logFile.writeln("=== LAYER_NAMES_END ===");',
            '    if (allLayers.length === 0) {',
            '        logFile.writeln("警告: PSD文件中没有找到任何图层！");',
            '        logFile.writeln("尝试其他方法检查图层...");',
            '        try {',
            '            logFile.writeln("检查图层集合属性:");',
            '            logFile.writeln("artLayers.length: " + psd.artLayers.length);',
            '            logFile.writeln("layers.length: " + psd.layers.length);',
            '            if (psd.layers && psd.layers.length > 0) {',
            '                logFile.writeln("使用layers属性找到图层:");',
            '                for (var i = 0; i < psd.layers.length; i++) {',
            '                    logFile.writeln("图层 " + (i+1) + ": " + psd.layers[i].name);',
            '                }',
            '            }',
            '        } catch (e) {',
            '            logFile.writeln("其他方法检查失败: " + e.message);',
            '        }',
            '        logFile.writeln("可能的原因:");',
            '        logFile.writeln("1. PSD文件是空的");',
            '        logFile.writeln("2. 所有图层都被隐藏");',
            '        logFile.writeln("3. 图层被锁定或不可编辑");',
            '        logFile.writeln("4. 图层在背景图层中");',
            '        logFile.writeln("建议: 请检查PSD文件是否包含可见的图层");',
            '    } else {',
            '        logFile.writeln("找到 " + allLayers.length + " 个图层，可以开始替换操作");',
            '    }',
            '    logFile.writeln("开始处理图层替换...");',
            '',
            '    // 处理文本替换',
            '    logFile.writeln("=== 文本替换开始 ===");',
        ]

        for item in text_replacements:
            jsx_lines.append(f'''
    logFile.writeln("尝试替换文本图层: {item['name']}");
    try {{
        // 递归查找图层（包括组中的图层、画板中的图层和layers属性中的图层）',
        var layer = null;
        // 先在画板中查找
        if (psd.artboards && psd.artboards.length > 0) {{
            for (var i = 0; i < psd.artboards.length; i++) {{
                layer = findLayerByName(psd.artboards[i].layers, "{item['name']}");
                if (layer) break;
            }}
        }}
        // 如果画板中没找到，在layers属性中查找
        if (!layer && psd.layers && psd.layers.length > 0) {{
            layer = findLayerByName(psd.layers, "{item['name']}");
        }}
        // 如果layers中没找到，在artLayers中查找
        if (!layer) {{
            layer = findLayerByName(psd.artLayers, "{item['name']}");
        }}
        if (layer) {{
            logFile.writeln("找到图层: {item['name']}, 图层类型: " + layer.typename + ", 图层种类: " + layer.kind);
            if (layer.kind == LayerKind.TEXT) {{
                try {{
                    var oldContent = layer.textItem.contents;
                    layer.textItem.contents = "{item['content']}";
                    logFile.writeln("文本图层替换成功: {item['name']} -> 原内容: '" + oldContent + "' -> 新内容: '{item['content']}'");
                    
                    // 应用偏移和缩放
                    var offsetX = {item['offset_x']};
                    var offsetY = {item['offset_y']};
                    var scaleX = {item['scale_x']};
                    var scaleY = {item['scale_y']};
                    
                    if (offsetX != 0 || offsetY != 0 || scaleX != 0 || scaleY != 0) {{
                        logFile.writeln("应用文本偏移和缩放: 偏移X=" + offsetX + ", 偏移Y=" + offsetY + ", 缩放X=" + scaleX + ", 缩放Y=" + scaleY);
                        
                        // 等待文本更新完成
                        logFile.writeln("等待文本更新完成...");
                        app.refresh(); // 刷新文档状态
                        var waitTime = 500; // 0.5秒
                        logFile.writeln("等待 " + waitTime + " 毫秒...");
                        $.sleep(waitTime);
                        logFile.writeln("文本更新等待完成");
                        
                        // 获取当前文本边界
                        var textBounds = layer.bounds;
                        logFile.writeln("文本当前边界: 左=" + textBounds[0] + ", 上=" + textBounds[1] + ", 右=" + textBounds[2] + ", 下=" + textBounds[3]);
                        
                        // 计算新的位置和大小
                        var currentWidth = textBounds[2] - textBounds[0];
                        var currentHeight = textBounds[3] - textBounds[1];
                        var newWidth = currentWidth + scaleX;
                        var newHeight = currentHeight + scaleY;
                        
                        // 应用位置偏移
                        if (offsetX != 0 || offsetY != 0) {{
                            layer.translate(offsetX, offsetY);
                            logFile.writeln("应用位置偏移: X=" + offsetX + ", Y=" + offsetY);
                        }}
                        
                        // 应用缩放
                        if (scaleX != 0 || scaleY != 0) {{
                            layer.resize(newWidth / currentWidth * 100, newHeight / currentHeight * 100, AnchorPosition.MIDDLECENTER);
                            logFile.writeln("应用缩放: 宽度比例=" + (newWidth / currentWidth * 100) + "%, 高度比例=" + (newHeight / currentHeight * 100) + "%");
                        }}
                        
                        logFile.writeln("文本偏移和缩放应用完成");
                    }} else {{
                        logFile.writeln("无需应用偏移和缩放");
                    }}
                }} catch (textError) {{
                    logFile.writeln("文本内容替换失败: " + textError.message);
                }}
            }} else {{
                logFile.writeln("图层不是文本类型: {item['name']}, 实际类型: " + layer.kind);
            }}
        }} else {{
            logFile.writeln("文本图层未找到: {item['name']}");
            logFile.writeln("可用图层列表:");
            for (var i = 0; i < allLayers.length; i++) {{
                logFile.writeln("  - " + allLayers[i].name);
            }}
        }}
    }} catch (e) {{ 
        logFile.writeln("文本图层替换失败: {item['name']}, 错误: " + e.message);
    }}
''')



        for item in image_replacements:
            item["escaped_path"] = item["content"].replace("\\", "\\\\")
            jsx_lines.append(f'''
    logFile.writeln("尝试替换图片图层: {item['name']}");
    try {{
        // 递归查找图层（包括组中的图层、画板中的图层和layers属性中的图层）',
        var layer = null;
        // 先在画板中查找
        if (psd.artboards && psd.artboards.length > 0) {{
            for (var i = 0; i < psd.artboards.length; i++) {{
                layer = findLayerByName(psd.artboards[i].layers, "{item['name']}");
                if (layer) break;
            }}
        }}
        // 如果画板中没找到，在layers属性中查找
        if (!layer && psd.layers && psd.layers.length > 0) {{
            layer = findLayerByName(psd.layers, "{item['name']}");
        }}
        // 如果layers中没找到，在artLayers中查找
        if (!layer) {{
            layer = findLayerByName(psd.artLayers, "{item['name']}");
        }}
        if (layer) {{
            logFile.writeln("找到图层: {item['name']}, 可见性: " + (layer.visible ? "可见" : "隐藏") + ", 锁定状态: " + (layer.locked ? "锁定" : "未锁定"));
            logFile.writeln("图层类型: " + layer.typename + ", 图层种类: " + layer.kind);
            
            // 尝试多种替换方法
            var success = false;

     
            
                        // 方法1: 使用placedLayerReplaceContents
try {{
    logFile.writeln("方法1: ");
    // 记录原图层的边界信息
    var originalBounds = layer.bounds;
    logFile.writeln("原图层边界: 左=" + originalBounds[0] + ", 上=" + originalBounds[1] + ", 右=" + originalBounds[2] + ", 下=" + originalBounds[3]);
    var originalWidth = originalBounds[2] - originalBounds[0];
    var originalHeight = originalBounds[3] - originalBounds[1];
    logFile.writeln("原图层尺寸: " + originalWidth + " x " + originalHeight);
    
    app.activeDocument.activeLayer = layer;
    var desc = new ActionDescriptor();
    desc.putPath(charIDToTypeID("null"), new File("{item["escaped_path"]}"));
    executeAction(stringIDToTypeID("placedLayerReplaceContents"), desc, DialogModes.NO);
    logFile.writeln("方法1成功: placedLayerReplaceContents");
    
    // 等待图层更新
    app.refresh();
    $.sleep(500);
    
    // 检查替换后的图层是否需要调整
    var replacedLayer = app.activeDocument.activeLayer;
    if (replacedLayer && replacedLayer.bounds) {{
        var newBounds = replacedLayer.bounds;
        logFile.writeln("替换后图层边界: 左=" + newBounds[0] + ", 上=" + newBounds[1] + ", 右=" + newBounds[2] + ", 下=" + newBounds[3]);
        
        // 如果尺寸不匹配，进行调整
        var newWidth = newBounds[2] - newBounds[0];
        var newHeight = newBounds[3] - newBounds[1];
        if (Math.abs(newWidth - originalWidth) > 1 || Math.abs(newHeight - originalHeight) > 1) {{
            logFile.writeln("尺寸不匹配，执行自动缩放调整...");
            
            // 计算缩放比例（用户提供的方法）
            // 1. 提取原始边界框的纵横比
            var originalAspectRatio = originalWidth / originalHeight;
            
            // 2. 计算缩放因子（主导维度优先策略）
            var scaleX = originalWidth / newWidth;
            var scaleY = originalHeight / newHeight;
            // 使用每个图像独立的智能处理参数
            var useFillStrategy = {item['use_fill_strategy']};
            var cropStrategy = "{item['crop_strategy']}";
            var scaleFactor = useFillStrategy ? Math.max(scaleX, scaleY) : Math.min(scaleX, scaleY);
            logFile.writeln("缩放策略: " + (useFillStrategy ? "填充策略（最大化覆盖）" : "包含策略（完全包含）"));
            logFile.writeln("缩放因子: " + scaleFactor);
            
            // 3. 计算目标尺寸（保持原始宽高比）
            var targetWidth = newWidth * scaleFactor;
            var targetHeight = newHeight * scaleFactor;
            
            // 4. 计算中心偏移量（中心点对齐）
            var currentCenterX = (newBounds[0] + newBounds[2]) / 2;
            var currentCenterY = (newBounds[1] + newBounds[3]) / 2;
            var originalCenterX = (originalBounds[0] + originalBounds[2]) / 2;
            var originalCenterY = (originalBounds[1] + originalBounds[3]) / 2;
            var centerOffsetX = originalCenterX - currentCenterX;
            var centerOffsetY = originalCenterY - currentCenterY;
            
            logFile.writeln("自动缩放参数：原始宽高=" + originalWidth + "x" + originalHeight + ", 替换后宽高=" + newWidth + "x" + newHeight);
            logFile.writeln("自动缩放因子=" + scaleFactor + ", 目标宽高=" + targetWidth + "x" + targetHeight);
            logFile.writeln("中心点偏移：X=" + centerOffsetX + ", Y=" + centerOffsetY);
            
            // 5. 应用缩放变换
            replacedLayer.resize(scaleFactor * 100, scaleFactor * 100, AnchorPosition.MIDDLECENTER);
            logFile.writeln("应用自动缩放完成：" + (scaleFactor * 100) + "%");
            
            // 6. 应用位置调整，确保中心点对齐
            replacedLayer.translate(centerOffsetX, centerOffsetY);
            logFile.writeln("应用中心点对齐偏移完成");
            
            // 7. 如果是填充策略，检查并处理溢出部分
            if (useFillStrategy) {{
                logFile.writeln("使用裁剪策略: " + cropStrategy);
                // 计算缩放后的边界
                var scaledBounds = replacedLayer.bounds;
                var scaledWidth = scaledBounds[2] - scaledBounds[0];
                var scaledHeight = scaledBounds[3] - scaledBounds[1];
                
                // 计算溢出量
                var overflowX = scaledWidth - originalWidth;
                var overflowY = scaledHeight - originalHeight;
                
                if (overflowX > 0 || overflowY > 0) {{
                    logFile.writeln("填充策略：检测到溢出，执行裁剪调整");
                    logFile.writeln("溢出量：X=" + overflowX + "px, Y=" + overflowY + "px");
                    
                    // 根据裁剪策略计算所需的额外偏移
                    var cropOffsetX = overflowX / 2; // X方向始终居中裁剪
                    var cropOffsetY;
                    
                    if (cropStrategy === "center") {{
                        // 居中裁剪：Y方向也居中
                        cropOffsetY = overflowY / 2;
                        logFile.writeln("使用居中裁剪策略");
                    }} else if (cropStrategy === "bottom") {{
                        // 优化的底部裁剪：Y方向向上偏移一定量，保护顶部头像和优化上半身排版
                        // 向上偏移量为溢出量的50%，确保顶部有适当留白
                        cropOffsetY = Math.min(overflowY * -0.50, overflowY); // 最多偏移50%的溢出量
                        logFile.writeln("使用优化的底部裁剪策略（保护顶部头像和优化上半身排版）");
                        logFile.writeln("向上偏移量：" + cropOffsetY + "px");
                    }}
                    
                    // 应用额外偏移以实现裁剪效果
                    replacedLayer.translate(-cropOffsetX, -cropOffsetY);
                    logFile.writeln("应用裁剪偏移：X=" + (-cropOffsetX) + "px, Y=" + (-cropOffsetY) + "px");
                    
                    // 记录裁剪后的位置
                    var croppedBounds = replacedLayer.bounds;
                    logFile.writeln("裁剪后图层边界: 左=" + croppedBounds[0] + ", 上=" + croppedBounds[1] + ", 右=" + croppedBounds[2] + ", 下=" + croppedBounds[3]);
                }}
            }}
            
            // 记录调整后的尺寸和位置
            var adjustedBounds = replacedLayer.bounds;
            logFile.writeln("自动调整后图层边界: 左=" + adjustedBounds[0] + ", 上=" + adjustedBounds[1] + ", 右=" + adjustedBounds[2] + ", 下=" + adjustedBounds[3]);
        }} else {{
            logFile.writeln("尺寸匹配，无需调整");
        }}
        
        // 验证最终位置
        var finalBounds = replacedLayer.bounds;
        logFile.writeln("最终边界: 左=" + finalBounds[0] + ", 上=" + finalBounds[1] + ", 右=" + finalBounds[2] + ", 下=" + finalBounds[3]);
        
        // 应用用户指定的偏移和缩放（方法1）
        var userOffsetX = {item['offset_x']};
        var userOffsetY = {item['offset_y']};
        var userScaleX = {item['scale_x']};
        var userScaleY = {item['scale_y']};
        
        if (userOffsetX != 0 || userOffsetY != 0 || userScaleX != 0 || userScaleY != 0) {{
            logFile.writeln("方法1 - 应用用户偏移和缩放: 偏移X=" + userOffsetX + ", 偏移Y=" + userOffsetY + ", 缩放X=" + userScaleX + ", 缩放Y=" + userScaleY);
            
            // 应用位置偏移
            if (userOffsetX != 0 || userOffsetY != 0) {{
                replacedLayer.translate(userOffsetX, userOffsetY);
                logFile.writeln("方法1 - 应用用户位置偏移: X=" + userOffsetX + ", Y=" + userOffsetY);
            }}
            
            // 应用缩放
            if (userScaleX != 0 || userScaleY != 0) {{
                var currentBounds = replacedLayer.bounds;
                var currentWidth = currentBounds[2] - currentBounds[0];
                var currentHeight = currentBounds[3] - currentBounds[1];
                var newWidth = currentWidth + userScaleX;
                var newHeight = currentHeight + userScaleY;
                
                replacedLayer.resize(newWidth / currentWidth * 100, newHeight / currentHeight * 100, AnchorPosition.MIDDLECENTER);
                logFile.writeln("方法1 - 应用用户缩放: 宽度比例=" + (newWidth / currentWidth * 100) + "%, 高度比例=" + (newHeight / currentHeight * 100) + "%");
            }}
            
            logFile.writeln("方法1 - 用户偏移和缩放应用完成");
                
                // 智能移动图像和调整视觉重量占比（根据用户提供的优化方法）
                try {{
                    // 1. 提取原始图层和新图层的边界框信息
                    var originalBounds = originalPlacedLayer.bounds;
                    var newBounds = replacedLayer.bounds;
                    
                    // 2. 中心点对齐偏移（精确居中）
                    var originalCenterX = (originalBounds[0] + originalBounds[2]) / 2;
                    var originalCenterY = (originalBounds[1] + originalBounds[3]) / 2;
                    var newCenterX = (newBounds[0] + newBounds[2]) / 2;
                    var newCenterY = (newBounds[1] + newBounds[3]) / 2;
                    
                    var centerOffsetX = originalCenterX - newCenterX;
                    var centerOffsetY = originalCenterY - newCenterY;
                    
                    if (Math.abs(centerOffsetX) > 1 || Math.abs(centerOffsetY) > 1) {{
                        replacedLayer.translate(centerOffsetX, centerOffsetY);
                        logFile.writeln("应用中心点对齐偏移: X=" + centerOffsetX + ", Y=" + centerOffsetY);
                        // 更新新边界框信息
                        newBounds = replacedLayer.bounds;
                    }}
                    
                    // 3. 优化头顶空间比例
                    var originalHeight = originalBounds[3] - originalBounds[1];
                    var newHeight = newBounds[3] - newBounds[1];
                    
                    // 理想头顶空间比例（根据人体测量学数据设置为12%）
                    var idealHeadroomRatio = 0.12;
                    var idealHeadroom = originalHeight * idealHeadroomRatio;
                    
                    // 计算当前头顶空间（图层顶部到原始图层顶部的距离）
                    var currentHeadroom = newBounds[1] - originalBounds[1];
                    
                    // 计算需要的垂直调整量
                    var verticalAdjustment = idealHeadroom - currentHeadroom;
                    
                    // 4. 视觉重量平衡调整
                    var doc = app.activeDocument;
                    var originalRulerUnits = app.preferences.rulerUnits;
                    app.preferences.rulerUnits = Units.PIXELS;
                    
                    // 新增：横图优化 - 检测是否为横图并适当放大
                    var isLandscape = (newBounds[2] - newBounds[0]) > (newBounds[3] - newBounds[1]) * 1.3; // 宽度明显大于高度时判断为横图
                    var landscapeScaleFactor = 1.0;
                    
                    if (isLandscape) {{
                        // 计算可以安全放大的比例，确保顶部对齐且不会裁剪头部
                        var availableSpace = originalHeight - (newBounds[3] - newBounds[1]);
                        landscapeScaleFactor = Math.min(1.2, 1 + (availableSpace * 0.7) / (newBounds[3] - newBounds[1])); // 最大放大到1.2倍，优先使用可用空间的70%
                        
                        // 应用横图优化缩放
                        var currentLandscapeWidth = newBounds[2] - newBounds[0];
                        var currentLandscapeHeight = newBounds[3] - newBounds[1];
                        var newLandscapeWidth = currentLandscapeWidth * landscapeScaleFactor;
                        var newLandscapeHeight = currentLandscapeHeight * landscapeScaleFactor;
                        
                        // 记录原始顶部位置，确保缩放后仍然顶部对齐
                        var originalTop = newBounds[1];
                        
                        // 执行缩放（以顶部为锚点）
                        replacedLayer.resize(newLandscapeWidth / currentLandscapeWidth * 100, newLandscapeHeight / currentLandscapeHeight * 100, AnchorPosition.TOPCENTER);
                        
                        // 更新边界信息
                        newBounds = replacedLayer.bounds;
                        
                        // 确保顶部对齐
                        var topAdjustment = originalTop - newBounds[1];
                        if (Math.abs(topAdjustment) > 1) {{
                            replacedLayer.translate(0, topAdjustment);
                            newBounds = replacedLayer.bounds; // 再次更新边界
                        }}
                        
                        logFile.writeln("应用横图优化缩放: 缩放因子=" + landscapeScaleFactor + "x");
                    }}
                    
                    var canvasCenterX = doc.width / 2;
                    var layerCenterX = (newBounds[0] + newBounds[2]) / 2;
                    
                    // 计算左右视觉重量不平衡导致的水平偏移
                    // 新增：卡通角色头部过大智能调整
                    var weightBalanceOffset = 0;
                    var defaultWeightAdjustmentStrength = 0.3;
                    
                    // 检测是否可能是卡通角色并评估头部大小
                    // 卡通角色通常头部占比更大（1/3到1/2或更多）
                    var isCartoonCharacter = false;
                    var estimatedHeadSizeRatio = 0.14; // 真人头部占比约14%
                    var cartoonHeadSizeRatio = 0.35; // 卡通角色头部占比通常更大（35%或更多）
                    var headRegionHeight = 0;
                    
                    // 计算图层高度和宽度
                    var layerHeight = newBounds[3] - newBounds[1];
                    var layerWidth = newBounds[2] - newBounds[0];
                    
                    // 根据特征判断是否为卡通角色并估算头部大小
                    if (isLandscape || layerWidth > layerHeight * 0.8) {{
                        // 对于横图或接近方形的图，检查是否可能是卡通角色
                        // 卡通角色通常有较大的头部
                        isCartoonCharacter = true;
                        
                        // 估算卡通角色头部大小（上部40%区域）
                        headRegionHeight = layerHeight * 0.4;
                        
                        // 判断头部是否过大
                        var isHeadTooBig = (headRegionHeight / layerHeight) > 0.35;
                        
                        if (isHeadTooBig) {{
                            // 头部过大时，降低视觉重量平衡调整强度
                            defaultWeightAdjustmentStrength = Math.max(0.1, defaultWeightAdjustmentStrength * 0.5);
                            logFile.writeln("检测到卡通角色头部过大: 降低平衡调整强度至 " + (defaultWeightAdjustmentStrength*100) + "%");
                        }}
                    }}
                    
                    // 计算最终的平衡偏移量
                    weightBalanceOffset = (canvasCenterX - layerCenterX) * defaultWeightAdjustmentStrength;
                    
                    // 应用综合调整
                    var finalOffsetX = weightBalanceOffset;
                    var finalOffsetY = verticalAdjustment;
                    
                    // 设置动态阈值，仅当调整量超过阈值时才执行操作
                    var threshold = 2; // 像素阈值
                    if (Math.abs(finalOffsetX) > threshold || Math.abs(finalOffsetY) > threshold) {{
                        replacedLayer.translate(finalOffsetX, finalOffsetY);
                        logFile.writeln("应用智能调整: X偏移=" + finalOffsetX + ", Y偏移=" + finalOffsetY);
                        logFile.writeln("  - 优化头顶空间比例: 目标=" + (idealHeadroomRatio*100) + "%, 当前=" + (currentHeadroom/originalHeight*100) + "%");
                        logFile.writeln("  - 平衡左右视觉重量: 调整强度=" + (defaultWeightAdjustmentStrength*100) + "%");
                        if (isCartoonCharacter) {{
                            logFile.writeln("  - 应用卡通角色优化: 已考虑头部比例特征");
                        }}
                    }}
                    
                    app.preferences.rulerUnits = originalRulerUnits;
                    logFile.writeln("智能调整完成");
                }} catch (e) {{
                    logFile.writeln("智能调整失败: " + e.message);
                }}
            }}
    }}
    
    success = true;
}} catch (e1) {{
    logFile.writeln("方法1失败: " + e1.message);
}}
            
            
    
            
            if (success) {{
                logFile.writeln("图片图层替换成功: {item['name']}");
                // 等待替换操作完成
                logFile.writeln("等待替换操作完成...");
                app.refresh(); // 刷新文档状态
                // 等待一段时间确保操作完成
                var waitTime = 1000; // 1秒
                logFile.writeln("等待 " + waitTime + " 毫秒...");
                $.sleep(waitTime);
                logFile.writeln("替换操作等待完成");
            }} else {{
                logFile.writeln("所有替换方法都失败了");
            }}
        }} else {{
            logFile.writeln("图片图层未找到: {item['name']}");
            logFile.writeln("可用图层列表:");
            for (var i = 0; i < allLayers.length; i++) {{
                logFile.writeln("  - " + allLayers[i].name);
            }}
        }}
    }} catch (e) {{ 
        logFile.writeln("图片图层替换失败: {item['name']}, 错误: " + e.message);
    }}
''')



        # 导出 PNG（可选）
        if export_png:
            jsx_lines.append(f'''
    logFile.writeln("准备导出PNG文件...");
    // 确保所有更改都已应用
    logFile.writeln("再次刷新文档状态...");
    app.refresh();
    var pngWaitTime = 500; // 1秒
    logFile.writeln("PNG导出前等待 " + pngWaitTime + " 毫秒...");
    $.sleep(pngWaitTime);
    
    logFile.writeln("正在导出PNG文件: {escaped_out_png_path}");
    var pngOpts = new PNGSaveOptions();
    psd.saveAs(new File("{escaped_out_png_path}"), pngOpts, true, Extension.LOWERCASE);
    $.sleep(500);
    logFile.writeln("---PNG文件导出成功---"); //写入这个标记，让python那边的节点能知道可以继续执行了

    logFile.close(); // 关闭文件强制写入缓存
    // 重新打开文件以追加模式继续写入后续日志
    logFile = new File("{escaped_log_path}");
    logFile.encoding = "UTF-8";
    logFile.open("a");
''')

        # 保存为新 PSD
        jsx_lines.append(f'''
    logFile.writeln("所有替换操作完成，准备保存...");
    // 最终刷新和等待
    logFile.writeln("执行最终刷新...");
    app.refresh();
    var finalWaitTime = 2000; // 2秒
    logFile.writeln("最终等待 " + finalWaitTime + " 毫秒...");
    $.sleep(finalWaitTime);
    logFile.writeln("开始保存文件...");
    
    logFile.writeln("正在保存PSD文件: {escaped_out_psd_path}");
    psd.saveAs(new File("{escaped_out_psd_path}"));
    logFile.writeln("PSD文件保存成功");
''')

        # 完成并关闭日志
        jsx_lines.append(f'''

    // 任务完成后关闭其他文档以节省内存
    if ({str(close_other_docs).lower()}) {{
        logFile.writeln("开始关闭其他文档...");
        try {{
            // 使用更安全的方式获取文档列表
            var allDocs = app.documents;
            logFile.writeln("当前打开的文档数量: " + allDocs.length);
            
            // 先收集需要关闭的文档名称
            var docsToClose = [];
            for (var i = 0; i < allDocs.length; i++) {{
                try {{
                    var docName = allDocs[i].name;
                    logFile.writeln("发现文档: " + docName + " (索引: " + i + ")");
                    if (docName !== currentDocName) {{
                        docsToClose.push(docName);
                    }} else {{
                        logFile.writeln("保留当前处理文档: " + docName);
                    }}
                }} catch (e) {{
                    logFile.writeln("获取文档信息失败 (索引: " + i + "): " + e.message);
                }}
            }}
            
            // 逐个关闭文档
            for (var j = 0; j < docsToClose.length; j++) {{
                var docNameToClose = docsToClose[j];
                logFile.writeln("尝试关闭文档: " + docNameToClose);
                try {{
                    // 重新获取文档对象
                    var currentDocs = app.documents;
                    for (var k = 0; k < currentDocs.length; k++) {{
                        if (currentDocs[k].name === docNameToClose) {{
                            currentDocs[k].close(SaveOptions.DONOTSAVECHANGES);
                            logFile.writeln("已关闭文档: " + docNameToClose);
                            break;
                        }}
                    }}
                }} catch (e) {{
                    logFile.writeln("关闭文档失败: " + docNameToClose + " - " + e.message);
                }}
            }}
            
            logFile.writeln("其他文档关闭完成，当前文档数量: " + app.documents.length);
        }} catch (e) {{
            logFile.writeln("关闭文档过程中出现错误: " + e.message);
        }}
    }} else {{
        logFile.writeln("跳过关闭其他文档（用户选择保留）");
    }}
    
    // 内存清理和优化
    logFile.writeln("开始内存清理...");
    try {{
        // 清理历史记录以减少内存占用
        app.purge(PurgeTarget.HISTORYCACHES);
        logFile.writeln("已清理历史记录缓存");
    }} catch (e) {{
        logFile.writeln("清理历史记录失败: " + e.message);
    }}
    
    try {{
        // 清理剪贴板
        app.purge(PurgeTarget.CLIPBOARDCACHE);
        logFile.writeln("已清理剪贴板缓存");
    }} catch (e) {{
        logFile.writeln("清理剪贴板失败: " + e.message);
    }}
    
    try {{
        // 清理撤销缓存
        app.purge(PurgeTarget.UNDOCACHES);
        logFile.writeln("已清理撤销缓存");
    }} catch (e) {{
        logFile.writeln("清理撤销缓存失败: " + e.message);
    }}
    
    logFile.writeln("内存清理完成");
    logFile.writeln("=== JSX脚本结束 ===");
    logFile.writeln("时间: " + new Date().toString());
    logFile.close();
''')

        # 添加try-catch块的结束
        jsx_lines.append('''
} catch (e) {
    logFile.writeln("错误: " + e.message);
    logFile.writeln("错误类型: " + typeof e);
    if (e.fileName) logFile.writeln("错误文件: " + e.fileName);
    if (e.lineNumber) logFile.writeln("错误行号: " + e.lineNumber);
    logFile.writeln("错误堆栈: " + e.stack);
    logFile.close();
}
logFile.writeln("日志文件已关闭");
''')

        # 不关闭Photoshop，保持打开状态以提高后续执行效率
        # jsx_lines.append('psd.close(SaveOptions.DONOTSAVECHANGES);')

        # 写入临时 JSX
        jsx_path = os.path.join(temp_dir, f"modify_psd_{timestamp}.jsx")
        
        # 打印JSX脚本内容用于调试
        print("=== 生成的JSX脚本内容 ===")
        # for i, line in enumerate(jsx_lines, 1):
        #     print(f"{i:2d}: {line}")
        # print("==========================")
        
        with open(jsx_path, "w", encoding="utf-8") as f:
            f.write("\n".join(jsx_lines))
        
        # 验证JSX文件是否成功创建
        if not os.path.exists(jsx_path):
            print(f"错误：JSX文件创建失败: {jsx_path}")
            return (None,)
        else:
            print(f"JSX文件创建成功: {jsx_path}")
            print(f"JSX文件大小: {os.path.getsize(jsx_path)} 字节")

        # 验证Photoshop路径是否存在
        if not os.path.exists(photoshop_path):
            print(f"错误：找不到Photoshop可执行文件: {photoshop_path}")
            print("请检查Photoshop安装路径是否正确，或者修改节点中的photoshop_path参数")
            return (None,)
        
        # 检查路径是否指向可执行文件
        if not photoshop_path.lower().endswith('.exe'):
            print(f"警告：路径可能不完整，请确保包含Photoshop.exe文件名")
            print(f"当前路径: {photoshop_path}")
            # 尝试自动补全路径
            if os.path.isdir(photoshop_path):
                possible_exe = os.path.join(photoshop_path, "Photoshop.exe")
                if os.path.exists(possible_exe):
                    photoshop_path = possible_exe
                    print(f"自动补全路径为: {photoshop_path}")
                else:
                    print("无法找到Photoshop.exe文件")
                    return (None,)
        
        # 执行Photoshop脚本
        try:
            print(f"正在执行Photoshop脚本: {jsx_path}")
            print(f"使用Photoshop路径: {photoshop_path}")
            
            # 使用subprocess.Popen执行，并监控日志文件
            print("开始执行Photoshop命令...")
            process = subprocess.Popen([photoshop_path, "-r", jsx_path], 
                                     stdout=subprocess.PIPE, 
                                     stderr=subprocess.PIPE)
            
            # 监控日志文件，等待JSX脚本完成
            print("开始监控JSX执行进度...")
            max_wait_time = 300  # 最大等待5分钟
            check_interval = 1   # 每2秒检查一次
            elapsed_time = 0
            
            while elapsed_time < max_wait_time:
                if os.path.exists(log_path):
                    try:
                        # 尝试多种编码格式
                        log_content = None
                        encodings = ['utf-8', 'gbk', 'gb2312', 'utf-8-sig', 'latin-1']
                        
                        for encoding in encodings:
                            try:
                                with open(log_path, 'r', encoding=encoding) as f:
                                    log_content = f.read()
                                break  # 如果成功读取，跳出编码尝试循环
                            except UnicodeDecodeError:
                                continue
                        
                        if log_content is not None:
                            if "---PNG文件导出成功---" in log_content:
                                print(f"检测到JSX脚本完成，总耗时: {elapsed_time}秒")
                                break
                        else:
                            print(f"无法读取日志文件，尝试了所有编码格式")
                    except Exception as e:
                        print(f"读取日志文件时出错: {e}")
                
                time.sleep(check_interval)
                elapsed_time += check_interval
                if elapsed_time % 10 == 0:  # 每10秒打印一次进度
                    print(f"等待JSX执行中... {elapsed_time}秒")
            
            if elapsed_time >= max_wait_time:
                print(f"JSX脚本执行超时（{max_wait_time}秒），强制终止进程")
                process.kill()
                process.wait()
                result = subprocess.CompletedProcess(
                    args=process.args,
                    returncode=-1,
                    stdout=b"",
                    stderr=b""
                )
            
            # # 等待进程结束
            # try:
            #     stdout, stderr = process.communicate(timeout=30)  # 最多等待30秒
            #     result = subprocess.CompletedProcess(
            #         args=process.args,
            #         returncode=process.returncode,
            #         stdout=stdout,
            #         stderr=stderr
            #     )
            # except subprocess.TimeoutExpired:
            #     print("进程等待超时，强制终止")
            #     process.kill()
            #     process.wait()
            #     result = subprocess.CompletedProcess(
            #         args=process.args,
            #         returncode=-1,
            #         stdout=b"",
            #         stderr=b""
            #     )
            
            # print("Photoshop命令执行完毕")
            
            # print(f"Photoshop执行完成，返回码: {result.returncode}")
            
            # # 输出所有stdout内容用于调试
            # if result.stdout:
            #     print("=== Photoshop输出内容 ===")
            #     print(result.stdout)
            #     print("========================")
            
            # # 输出所有stderr内容用于调试
            # if result.stderr:
            #     print("=== Photoshop错误内容 ===")
            #     print(result.stderr)
            #     print("========================")
            
            # 读取并显示日志文件（日志文件已经在监控过程中确认存在）
            if os.path.exists(log_path):
                print("=== Photoshop执行日志 ===")
                try:
                    # 尝试多种编码格式
                    log_content = None
                    encodings = ['utf-8', 'gbk', 'gb2312', 'utf-8-sig', 'latin-1']
                    
                    for encoding in encodings:
                        try:
                            with open(log_path, 'r', encoding=encoding) as f:
                                log_content = f.read()
                            print(f"成功使用编码: {encoding}")
                            break
                        except UnicodeDecodeError:
                            continue
                    
                    if log_content is not None:
                        print(log_content)
                    else:
                        print("无法读取日志文件，尝试了所有编码格式")
                except Exception as e:
                    print(f"读取日志文件失败: {e}")
                print("========================")
                
                # 解析图层名称
                layer_names = self.parse_layer_names(log_content)
                if layer_names:
                    print("=== PSD文件中的图层名称 ===")
                    for i, name in enumerate(layer_names, 1):
                        print(f"{i}. {name}")
                    print("==========================")
                else:
                    print("PSD文件中没有找到图层（图层总数: 0）")
            else:
                print(f"未找到日志文件: {log_path}")
                print("请检查Photoshop是否有权限写入该目录")
            
            # 检查是否有日志文件生成来判断是否成功
            if os.path.exists(log_path):
                print("Photoshop脚本执行成功（检测到日志文件）")
            elif result.returncode != 0:
                print(f"Photoshop execution failed with return code: {result.returncode}")
                print("请检查PSD文件路径是否正确，以及Photoshop是否能正常打开该文件")
            else:
                print("Photoshop脚本执行成功")
                
        except FileNotFoundError:
            print(f"错误：找不到Photoshop可执行文件: {photoshop_path}")
            print("请检查以下可能的Photoshop安装路径：")
            print("- C:\\Program Files\\Adobe\\Adobe Photoshop 2020\\Photoshop.exe")
            print("- C:\\Program Files\\Adobe\\Adobe Photoshop 2021\\Photoshop.exe")
            print("- C:\\Program Files\\Adobe\\Adobe Photoshop 2022\\Photoshop.exe")
            print("- C:\\Program Files\\Adobe\\Adobe Photoshop 2023\\Photoshop.exe")
            print("- C:\\Program Files\\Adobe\\Adobe Photoshop 2024\\Photoshop.exe")
        except PermissionError:
            print(f"错误：权限不足，无法执行Photoshop: {photoshop_path}")
            print("可能的解决方案：")
            print("1. 以管理员身份运行ComfyUI")
            print("2. 检查Photoshop是否正在运行，请先关闭Photoshop")
            print("3. 检查Windows Defender或杀毒软件是否阻止了执行")
            print("4. 尝试手动运行Photoshop确认路径正确")
        except subprocess.CalledProcessError as e:
            print(f"Photoshop执行失败，返回码: {e.returncode}")
            print(f"命令: {e.cmd}")
            if e.stdout:
                print("=== 标准输出 ===")
                print(e.stdout)
            if e.stderr:
                print("=== 错误输出 ===")
                print(e.stderr)
        except Exception as e:
            print(f"Photoshop execution failed: {e}")
            print(f"错误类型: {type(e).__name__}")

        # 返回 PNG 预览（如果存在）
        if export_png and os.path.exists(out_png_path):
            try:
                i = Image.open(out_png_path)
                image = i.convert("RGB")
                image = np.array(image).astype(np.float32) / 255.0
                image = torch.from_numpy(image)[None,]
                return (image,)
            except Exception as e:
                print(f"处理预览图像时出错: {e}")
                import traceback
                traceback.print_exc()
                return (None,)
        else:
            print(f"未找到输出文件: {out_png_path}")
            return (None,)


class LoadImageRGBA:
    @classmethod
    def INPUT_TYPES(s):
        input_dir = folder_paths.get_input_directory()
        files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
        return {"required":
                    {"image": (sorted(files), {"image_upload": True})},
                }

    CATEGORY = "image"

    RETURN_TYPES = ("IMAGE", "MASK")
    FUNCTION = "load_image"
    
    def load_image(self, image):
        image_path = folder_paths.get_annotated_filepath(image)
        
        img = node_helpers.pillow(Image.open, image_path)
        
        output_images = []
        output_masks = []
        w, h = None, None

        excluded_formats = ['MPO']
        
        for i in ImageSequence.Iterator(img):
            i = node_helpers.pillow(ImageOps.exif_transpose, i)

            if i.mode == 'I':
                i = i.point(lambda i: i * (1 / 255))
            
            # 检查是否有alpha通道
            has_alpha = 'A' in i.getbands()
            
            if has_alpha:
                # 保持RGBA格式
                image = i.convert("RGBA")
                print(f"检测到RGBA图像，保留alpha通道")
            else:
                # 转换为RGB
                image = i.convert("RGB")
                print(f"检测到RGB图像，无alpha通道")

            if len(output_images) == 0:
                w = image.size[0]
                h = image.size[1]
            
            if image.size[0] != w or image.size[1] != h:
                continue
            
            # 转换为numpy数组
            image_array = np.array(image).astype(np.float32) / 255.0
            
            # 根据通道数处理
            if has_alpha and image_array.shape[2] == 4:
                # RGBA格式，保持4通道
                image_tensor = torch.from_numpy(image_array)[None,]
                # 创建mask（alpha通道的反转）
                mask = 1. - torch.from_numpy(image_array[:,:,3])
            else:
                # RGB格式，转换为3通道
                image_tensor = torch.from_numpy(image_array)[None,]
                # 创建空的mask
                mask = torch.zeros((image_array.shape[0], image_array.shape[1]), dtype=torch.float32, device="cpu")
            
            output_images.append(image_tensor)
            output_masks.append(mask.unsqueeze(0))

        if len(output_images) > 1 and img.format not in excluded_formats:
            output_image = torch.cat(output_images, dim=0)
            output_mask = torch.cat(output_masks, dim=0)
        else:
            output_image = output_images[0]
            output_mask = output_masks[0]

        print(f"LoadImageRGBA: 输出图像形状: {output_image.shape}")
        return (output_image, output_mask)

    @classmethod
    def IS_CHANGED(s, image):
        image_path = folder_paths.get_annotated_filepath(image)
        m = hashlib.sha256()
        with open(image_path, 'rb') as f:
            m.update(f.read())
        return m.digest().hex()

    @classmethod
    def VALIDATE_INPUTS(s, image):
        if not folder_paths.exists_annotated_filepath(image):
            return "Invalid image file: {}".format(image)
        return True


NODE_CLASS_MAPPINGS = {
    "ModifyPSDLayerFixed": ModifyPSDLayer,
    "LoadImageRGBA": LoadImageRGBA,
}

NODE_DISPLAY_NAME_MAPPINGS = {
    "ModifyPSDLayerFixed": "Modify PSD Layer (3 Text / 4 Image + Export)",
    "LoadImageRGBA": "Load Image (RGBA)",
}
